package syntax;

//import com.modeliosoft.modelio.javadesigner.annotations.objid;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import parameters.Parameters;
import semantics.RunStep;
import semantics.State;

//@objid ("0049e080-d50f-10da-ac48-082e5f2773a2")
public class Transition extends NamedObject implements Cloneable {

    @Override
    public int compareTo(NamedObject t) {
        Transition other = (Transition) (t);
        int res = 0;
        //comparing src locations
        res = this.srcLocation.compareTo(other.getSrcLocation());
        if (res != 0) {
            return res;
        }
        res = this.destLocation.compareTo(other.getDestLocation());
        if (res != 0) {
            return res;
        }
        res = this.message.compareTo(other.getMessage());
        if (res != 0) {
            return res;
        }
        res = this.clockGuard.compareTo(other.getClockGuard());
        if (res != 0) {
            return res;
        }
        Collections.sort(resetClocks);
        Collections.sort(other.resetClocks);
        res = resetClocks.toString().compareTo(other.getResetClocks().toString());
        return res;

//        return super.compareTo(t); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }
        return compareTo((Transition) o) == 0;
    }
    //@objid ("0045b712-d64c-10da-ac48-082e5f2773a2")
    private Location srcLocation;
    //@objid ("0017b40c-d653-10da-ac48-082e5f2773a2")
    private Location destLocation;
    //@objid ("0007eb3a-e506-10da-ac48-082e5f2773a2")
    private Message message;
    //@objid ("006785e0-e529-10da-ac48-082e5f2773a2")
    private ClockGuard clockGuard;
    private List<Clock> resetClocks;
    private boolean reachable;
    
    private State lastState;
    private Collection<State> lastStatesPool;

    @Override
    public Object clone() {
        Transition res = null;
        
        ArrayList<Clock> clonedResetClocks = null;
        ClockGuard clonedClockGuard = null;
        Collection<State> clonedLastStatesPool = null;
        try {
            if (resetClocks != null) {
                clonedResetClocks = (ArrayList<Clock>) (((ArrayList<Clock>) resetClocks).clone());
            }
            if (clockGuard != null) {
                clonedClockGuard = (ClockGuard) (clockGuard.clone());
            }
            if (lastStatesPool != null) {
                clonedLastStatesPool = ((LinkedList<State>) ((LinkedList<State>)(lastStatesPool)).clone());
            }
            res = new Transition(srcLocation, destLocation, message,
                    clonedClockGuard,
                    clonedResetClocks, reachable, getName(), clonedLastStatesPool);
        } catch (CloneNotSupportedException ex) {
            Logger.getLogger(Transition.class.getName()).log(Level.SEVERE, null, ex);
        }
        return res;
    }

    
    private void initClockGuard() {
        clockGuard = new ClockGuard();
    }
    
    private void initLastStatesPool() {
        lastStatesPool = new LinkedList<State>();
    }

    private void initResetClocks() {
        resetClocks = new ArrayList<Clock>();
    }
    
    public Transition(Location srcLocation, Location destLocation, Message message,
            ClockGuard clockGuard, List<Clock> resetClocks, boolean reachable, String name) {
        super(name);
        this.srcLocation = srcLocation;
        this.destLocation = destLocation;
        this.message = message;
        this.clockGuard = clockGuard;
        this.resetClocks = resetClocks;
        this.reachable = reachable;
        initLastStatesPool();
    }
    
    public Transition(Location srcLocation, Location destLocation, Message message,
            ClockGuard clockGuard, List<Clock> resetClocks, boolean reachable, String name, Collection<State> lastStatesPool) {
        super(name);
        this.srcLocation = srcLocation;
        this.destLocation = destLocation;
        this.message = message;
        this.clockGuard = clockGuard;
        this.resetClocks = resetClocks;
        this.reachable = reachable;
        this.lastStatesPool = lastStatesPool;
    }

    public void addConditionAtom(ClockConditionAtom atom) {
        if (clockGuard == null) {
            clockGuard = new ClockGuard();
        }
        clockGuard.addClockConditionAtom(atom);
    }

    public void delConditionAtom(ClockConditionAtom atom) {
        if (clockGuard == null || (!clockGuard.containsClock(atom.getClock()))) {
            return;
        }
        clockGuard.getConditionAtom().remove(atom);
    }

    public Location getSrcLocation() {
        return srcLocation;
    }

    public void setSrcLocation(Location srcLocation) {
        this.srcLocation = srcLocation;
    }

    public Location getDestLocation() {
        return destLocation;
    }

    public void setDestLocation(Location destLocation) {
        //TODO: modify in going and outgoing
        //destLocation.getIngoingTrans().add(this);
        this.destLocation = destLocation;
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    public ClockGuard getClockGuard() {
        return clockGuard;
    }

    public void setClockGuard(ClockGuard clockGuard) {
        this.clockGuard = clockGuard;
    }

    public boolean isReachable() {
        return reachable;
    }

    public void setReachable(boolean reachable) {
        this.reachable = reachable;
    }

    public void addResetClock(Clock clock) {
        resetClocks.add(clock);
    }

    public Transition(String name) {
        super(name);
        reachable = false;
        resetClocks = new ArrayList<Clock>();
        initClockGuard();
        initResetClocks();
        initLastStatesPool();
    }

    public Transition(String name, Location srcLocation, Location destLocation, Message message, ClockGuard clockGuard) {
        super(name);
        this.srcLocation = srcLocation;
        this.destLocation = destLocation;
        this.message = message;
        this.clockGuard = clockGuard;
        this.reachable = false;
        initResetClocks();
        initLastStatesPool();
    }

    @Override
    public String toString() {
        return getName() + ":" + srcLocation + "->" + message + "->" + clockGuard + "->" + resetClocks + "->" + destLocation;
        //    return getName();
    }

    public List<Clock> getResetClocks() {
        return resetClocks;
    }

    public void setResetClocks(List<Clock> resetClocks) {
        this.resetClocks = resetClocks;
    }

    public Float minDelay(State currentState) {
        float res = 0F;
        float v_x;
        float delay = 0F;

        ClockGuard guard = this.getClockGuard();
        if (guard == null) {
            return 0F;
        }
        for (ClockConditionAtom atom : guard.getConditionAtom()) {
            v_x = currentState.getValuation().get(atom.getClock());
            if (atom.getLowerRelation() == Operator.Op.getLT()) {
                delay = atom.getDomain().getL_b() - v_x + Parameters.getEpsilon();
            } else {
                delay = atom.getDomain().getL_b() - v_x;
            }
            if (delay > res) {
                res = delay;
            }
        }

        return res;
    }

    public Float maxDelay(State currentState) {
        float res = Parameters.getInfinity();
        float v_x;
        float delay = 0F;

        ClockGuard guard = this.getClockGuard();
        if (guard == null) {
            return 0F;
        }
        for (ClockConditionAtom atom : guard.getConditionAtom()) {
            v_x = currentState.getValuation().get(atom.getClock());
            if (atom.getUpperRelation() == Operator.Op.getLT()) {
                delay = atom.getDomain().getU_b() - v_x - Parameters.getEpsilon();
            } else {
                delay = atom.getDomain().getU_b() - v_x;
            }
            if (delay < res) {
                res = delay;
            }
        }

        return res;
    }

    public State getLastState() {
        return lastState;
    }

    public void setLastState(State lastState) {
        this.lastState = lastState;
    }
    
    public void addLastStateToPool(State state) {
        lastStatesPool.add(state);
    }
    
    public void removeLastStateFromPool(State state) {
        lastStatesPool.remove(state);
    }
    
    public void removeAllFromPool() {
        lastStatesPool.clear();
    }
    
    public Collection<State> getLastStatesPool() {
        return lastStatesPool;
    }

    @Override
    public int hashCode() {
        return getName().hashCode();
    }
    
    
}
